package com.gmrz.uaf.servlet.v1.handler;

import com.gmrz.service.challengestore.ChallengeStoreException;
import com.gmrz.service.challengestore.ChallengeStoreService;
import com.gmrz.uaf.common.*;
import com.gmrz.uaf.common.service.challengestore.UAFChallengeStoreServiceProvider;
import com.gmrz.uaf.db.DAOException;
import com.gmrz.uaf.db.UAFDAOFactory;
import com.gmrz.uaf.db.UAFDBConnectionMgr;
import com.gmrz.uaf.db.dao.UasConfigDAO;
import com.gmrz.uaf.db.dao.WhiteListDAO;
import com.gmrz.uaf.protocol.v1.json.UAFSchemaBuilder;
import com.gmrz.uaf.protocol.v1.processor.exception.AdditionUviException;
import com.gmrz.uaf.protocol.v1.processor.exception.UAFErrorCode;
import com.gmrz.uaf.protocol.v1.processor.exception.WhiteListDenyException;
import com.gmrz.uaf.protocol.v1.schema.DeviceDetails;
import com.gmrz.uaf.protocol.v1.schema.MatchCriteria;
import com.gmrz.uaf.protocol.v1.schema.RegistrationRequest;
import com.gmrz.uaf.protocol.v1.schema.WhiteList;
import com.gmrz.uaf.servlet.v1.response.FidoRespPayload;
import com.gmrz.uaf.servlet.v1.validation.PayloadValidationException;
import com.gmrz.uaf.server.ServerConfig;
import com.gmrz.uaf.server.ServerContext;
import com.gmrz.uas.plugin.exception.PluginGoOnBusinessException;
import com.gmrz.util.Convert;
import com.gmrz.util.Strings;
import com.gmrz.util.db.DBUtil;
import com.gmrz.webauthn.protocol.v1.processor.WebAuthnException;
import com.gmrz.webauthn.protocol.v1.schema.PublicKeyCredentialCreationOptions;
import com.gmrz.webauthn.request.WebAuthnRequestContext;
import com.gmrz.webauthn.request.WebAuthnRespPayload;
import com.google.gson.Gson;
import com.google.gson.JsonParseException;
import com.google.gson.stream.JsonReader;
import org.apache.commons.codec.binary.Hex;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.IOException;
import java.io.StringReader;
import java.security.MessageDigest;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;

public class UAFBaseHandler {
    private static final Logger LOG = LogManager.getLogger(UAFBaseHandler.class);
    protected ServerConfig serverConfig = GuiceUtil.getProcessorInjector().getInstance(ServerConfig.class);

    public ServerConfig getServerConfig() {
        return serverConfig;
    }

    public String buildReceiveResponse(Gson gson, FidoRespPayload<String> respPayload,
                                       List<? extends Object> requests) {
        String uafRequest = null;
        int statusCode = Constants.StatusCode.INTERNAL_SERVER_ERROR.getCode();
        String description = "Server returned empty response.";
        if ((requests != null) && (!requests.isEmpty())) {
            uafRequest = gson.toJson(requests);
            statusCode = Constants.StatusCode.OK.getCode();
            description = "OK";
            if(ServerContext.getServerContext().isBase64()){
                respPayload.setUafRequest(Convert.toBase64(uafRequest.getBytes()));
            }else {
                respPayload.setUafRequest(uafRequest);
            }
            respPayload.setStatusCode(statusCode);
            respPayload.setDescription(description);
            return gson.toJson(respPayload);
        }
        respPayload.setUafRequest(uafRequest);
        respPayload.setStatusCode(statusCode);
        respPayload.setDescription(description);
        return gson.toJson(respPayload);
    }

    public String buildUpdateCertReceiveResponse(Gson gson, FidoRespPayload<String> respPayload,
                                                 List<? extends Object> requests) {
        String uafRequest = null;
        int statusCode = Constants.StatusCode.INTERNAL_SERVER_ERROR.getCode();
        String description = "Server returned empty response.";
        if ((requests != null) && (!requests.isEmpty())) {
            uafRequest = gson.toJson(requests);
            statusCode = Constants.StatusCode.UPDATECERT.getCode();
            description = "Continue to update certificate";
            respPayload.setUafRequest(uafRequest);
            respPayload.setStatusCode(statusCode);
            respPayload.setDescription(description);
            return gson.toJson(respPayload);
        }
        respPayload.setUafRequest(uafRequest);
        respPayload.setStatusCode(statusCode);
        respPayload.setDescription(description);
        return gson.toJson(respPayload);
    }

    public String buildFidoRespFromException(FidoRespPayload payload, Exception ex) {
        Gson gson = UAFSchemaBuilder.getGson();
        payload.setUafRequest(null);
        Tuple tuple = iterateExceptions(ex);
        payload.setStatusCode(tuple.statusCode);
        payload.setDescription(tuple.description);
        payload.setAdditionuvi(tuple.data);
        return gson.toJson(payload);
    }


    public String buildWebAuthnResponse(Gson gson, WebAuthnRespPayload<WebAuthnRequestContext> respPayload,
                                        PublicKeyCredentialCreationOptions publicKeyCredentialCreationOptions) {
        if (publicKeyCredentialCreationOptions != null) {
            respPayload.setStatusCode(Constants.StatusCode.OK.getCode());
            respPayload.setDescription("OK");
            WebAuthnRequestContext webAuthnRequestContext = GuiceUtil.getProcessorInjector().getInstance(WebAuthnRequestContext.class);
            webAuthnRequestContext.setOptions(publicKeyCredentialCreationOptions);

            String serverData = gson.toJson(publicKeyCredentialCreationOptions);
            webAuthnRequestContext.setServerData(Convert.toBase64(serverData.getBytes()));

            respPayload.setWebAuthnRequest(webAuthnRequestContext);

            return gson.toJson(respPayload);
        } else {
            respPayload.setStatusCode(Constants.StatusCode.INTERNAL_SERVER_ERROR.getCode());
            respPayload.setDescription("Server returned empty response.");
        }
        return gson.toJson(respPayload);
    }

    public String buildWebAuthnRespFromException(WebAuthnRespPayload payload, Exception ex) {
        Gson gson = UAFSchemaBuilder.getGson();
        Tuple tuple = iterateExceptions(ex);
        payload.setStatusCode(tuple.statusCode);
        payload.setDescription(tuple.description);
        return gson.toJson(payload);
    }

    public String buildFidoRespHasUafRequestFromException(FidoRespPayload payload, Exception ex) {
        Gson gson = UAFSchemaBuilder.getGson();
        Tuple tuple = iterateExceptions(ex);
        payload.setStatusCode(tuple.statusCode);
        payload.setDescription(tuple.description);
        payload.setAdditionuvi(tuple.data);
        return gson.toJson(payload);
    }

    Tuple iterateExceptions(Exception ex) {
        Tuple tuple = new Tuple();
        tuple.statusCode = Constants.StatusCode.BAD_REQ.getCode();
        tuple.description = "Server did not understand the content of request.";
        if ((ex instanceof AdditionUviException)) {
            tuple.statusCode = ((AdditionUviException) ex).getErrorCode().getStatusCode();
            tuple.description = ((AdditionUviException) ex).getErrorCode().getMessage();
            tuple.data = ((AdditionUviException) ex).getData();
        } else if ((ex instanceof PluginGoOnBusinessException)) {
            tuple.statusCode = UAFErrorCode.PROTOCOL_GO_ON_BUSINESS.getStatusCode();
            tuple.description = ex.getMessage();
        } else if ((ex instanceof PayloadValidationException)) {
            PayloadValidationException pve = (PayloadValidationException) ex;
            tuple.statusCode = Constants.StatusCode.UNACCEPTABLE_CONTENT.getCode();
            tuple.description = pve.getValidationErrorMessage();
        } else if ((ex instanceof UAFException)) {
            UAFException uafe = (UAFException) ex;
            IErrorCode ec = uafe.getErrorCode();
            if (ec != null) {
                tuple.statusCode = ec.getStatusCode();
                tuple.description = ec.getMessage();
            }
        } else if ((ex instanceof JsonParseException)) {
            tuple.statusCode = Constants.StatusCode.BAD_REQ.getCode();
        } else if ((ex instanceof UAFRuntimeException)) {
            tuple.statusCode = Constants.StatusCode.BAD_REQ.getCode();
            tuple.description = ex.getMessage();
        } else if ((ex instanceof UAFCommonRuntimeException)) {
            UAFCommonRuntimeException uafe = (UAFCommonRuntimeException) ex;
            IErrorCode ec = uafe.getErrorCode();
            if (ec != null) {
                tuple.statusCode = ec.getStatusCode();
                tuple.description = ec.getMessage();
            }
        } else if (ex instanceof WebAuthnException){
            WebAuthnException uafe = (WebAuthnException) ex;
            IErrorCode ec = uafe.getErrorCode();
            if (ec != null) {
                tuple.statusCode = ec.getStatusCode();
                tuple.description = ec.getMessage();
            }
        }
        return tuple;
    }

    protected void logRequest(String request, String transNo, String userName, String appID, String authType, String transType,
                              Constants.Method method) throws WhiteListDenyException {
        ServerContext serverContext = ServerContext.getServerContext();
        serverContext.getFidoLog().setRequest(request);
        serverContext.getFidoLog().setOperType(method.getOperType());
        serverContext.getFidoLog().setAppID(appID);
        serverContext.getFidoLog().setUserName(userName);
        serverContext.getFidoLog().setTransNo(transNo);
        serverContext.getFidoLog().setAuthType(authType);
        serverContext.getFidoLog().setTransType(transType);
        try {
            ServerConfig serverConfig = GuiceUtil.getProcessorInjector().getInstance(ServerConfig.class);
            if (serverConfig.isWhiteListSwitch()) {
                if (!isAllowedByWhiteList(serverContext.getFidoLog().getRemoteIP(),
                        serverContext.getFidoLog().getAppID())) {
                    throw new WhiteListDenyException(UAFErrorCode.BIZ_WHITE_LIST_FORBIDDEN);
                }
            }
        } catch (DAOException e) {
            LOG.error(e);
            throw new WhiteListDenyException(UAFErrorCode.BIZ_WHITE_LIST_FORBIDDEN, e);
        }
    }

    private boolean isAllowedByWhiteList(String ip, String appID) throws DAOException {

        UAFChallengeStoreServiceProvider uafChallengeStoreServiceProvider = GuiceUtil.getProcessorInjector()
                .getInstance(UAFChallengeStoreServiceProvider.class);

        ChallengeStoreService challengeStoreService = uafChallengeStoreServiceProvider.get();
        List<WhiteList> whiteList = null;
        try {
            whiteList = challengeStoreService.getWhiteList(Constants.WHITE_LIST_KEY);
        } catch (Exception e) {
            LOG.error(e);
        }
        if (whiteList == null || whiteList.isEmpty()) {

            Connection conn = null;
            try {
                conn = UAFDBConnectionMgr.getConnection(true);
                WhiteListDAO dao = UAFDAOFactory.createWhiteListDAO(conn);
                whiteList = dao.list();
            } catch (SQLException sqle) {
                throw new DAOException(UAFErrorCode.DB_DATABASE_CONNECTION_ERROR, sqle);
            } finally {
                DBUtil.close(conn);
            }
        }
        if (whiteList == null || whiteList.isEmpty())
            return true;
        try {
            challengeStoreService.setWhiteList(Constants.WHITE_LIST_KEY, whiteList);
        } catch (ChallengeStoreException e) {
            LOG.error(e);
        }
        for (WhiteList w : whiteList) {
            if (ip.equals(w.getVisitAddress()) && appID.equals(w.getAppID()))
                return true;
        }
        return false;

    }

    /**
     * 根据客户的三个标识纬度,生成客户唯一标识
     *
     * @param custNo 客户号
     * @param rf1    客户标识预留字段1
     * @param rf2    客户标识预留字段2
     * @return
     * @throws UAFRuntimeException
     */
    protected String generateHashedUserName(String custNo, String rf1, String rf2) throws UAFRuntimeException {

        String plainUserName = generatePlainUserName(custNo, rf1, rf2);
        //String plainUserName = custNo;
        try {
            // 指定获取 SHA-1 算法的 MessageDigest对象
            MessageDigest messageDigest = MessageDigest.getInstance("sha-1");
            // 对指定的字节数组完成摘要计算
            byte[] hash = messageDigest.digest(plainUserName.getBytes());
            LOG.info("Generate user identity={}", Hex.encodeHexString(hash));
            // 将byte[]转为16进制字符串
            return Hex.encodeHexString(hash);
        } catch (Exception e) {
            LOG.error("{} {}", "Generate user identity failed", e);
            throw new UAFRuntimeException("Generate user identity failed", e);
        }
    }

    protected String generatePlainUserName(String userName, String rf1, String rf2) {
        StringBuilder builder = new StringBuilder(userName);
        if (rf1 != null && rf1.length() > 0) {
            builder.append(rf1);
        }
        if (rf2 != null && rf2.length() > 0) {
            builder.append(rf2);
        }
        return builder.toString();
    }

    protected static class Tuple {
        int statusCode;
        String description;
        String data;
    }

    /**
     * 获取发起接口和完成接口共同的参数做一致校验
     *
     * @param userName
     * @param rf1
     * @param rf2
     * @param appID
     * @param deviceID
     * @param transType
     * @param authTypes
     * @return
     */
    protected String getValidateData(String userName, String rf1, String rf2, String appID, String deviceID, String transType, List<String> authTypes, String challenge) {
        StringBuilder sb = new StringBuilder();

        sb.append("userName:");
        sb.append(userName);
        sb.append(",");

        sb.append("rf1:");
        sb.append(rf1);
        sb.append(",");

        sb.append("rf2:");
        sb.append(rf2);
        sb.append(",");

        sb.append("appID:");
        sb.append(appID);
        sb.append(",");

        sb.append("deviceID:");
        sb.append(deviceID);
        sb.append(",");

        sb.append("transType:");
        sb.append(transType);
        sb.append(",");

        StringBuilder authTypeString = new StringBuilder();
        if (null != authTypes && !authTypes.isEmpty()) {
            List<String> authTypeList = new ArrayList<String>();
            authTypeList.addAll(authTypes);
            Collections.sort(authTypeList);
            for (String at : authTypeList) {
                authTypeString.append(at);
            }
        }
        sb.append("authType:");
        sb.append(authTypeString.toString());

        sb.append(",");
        sb.append("challenge:");
        sb.append(challenge);


        return sb.toString();

    }

    /**
     * 获取发起接口和完成接口共同的参数做一致校验
     *
     * @param userName
     * @param rf1
     * @param rf2
     * @param appID
     * @param deviceID
     * @param transType
     * @param authType
     * @return
     */
    protected String getValidateData(String userName, String rf1, String rf2, String appID, String deviceID, String transType, String authType, String challenge) {
        List<String> authTypes = new ArrayList<String>();
        authTypes.add(authType);
        return getValidateData(userName, rf1, rf2, appID, deviceID, transType, authTypes, challenge);
    }

    /**
     * 验证白名单和黑名单
     *
     * @param devices
     */
    protected void validateDevice(DeviceDetails devices, String appID) throws UAFException {
        if (null != devices) {
            LOG.debug("validate device:" + devices.getDeviceType() + "," + devices.getDeviceName());
            String deviceType = devices.getDeviceType();//厂商
            String deviceName = devices.getDeviceName();//设备
            if (deviceName == null){
                deviceName = Constants.UAF_FORBID_ALLOW_CONCAT;
            }else {
                deviceName = deviceName.trim();
            }
            String deviceVersion = devices.getDeviceVersion();
            if (deviceVersion == null){
                deviceVersion = Constants.UAF_FORBID_ALLOW_CONCAT;
            }else {
                deviceVersion = deviceVersion.trim();
            }

            // 拼接 厂商,设备型号,版本
            StringBuilder strB = new StringBuilder();
            strB.append(deviceType);
            strB.append(Constants.UAF_FORBID_ALLOW_CONCAT_SPLIT);
            strB.append(deviceName);
            strB.append(Constants.UAF_FORBID_ALLOW_CONCAT_SPLIT);
            strB.append(deviceVersion);
            String vendor_quipment_version = strB.toString();
            strB.delete(0, strB.length());
            // 拼接 厂商,设备型号,
            strB.append(deviceType);
            strB.append(Constants.UAF_FORBID_ALLOW_CONCAT_SPLIT);
            strB.append(deviceName);
            strB.append(Constants.UAF_FORBID_ALLOW_CONCAT_SPLIT);
            strB.append(Constants.UAF_FORBID_ALLOW_CONCAT);
            String vendor_quipment = strB.toString();
            strB.delete(0, strB.length());
            // 拼接 厂商,,
            strB.append(deviceType);
            strB.append(Constants.UAF_FORBID_ALLOW_CONCAT_SPLIT);
            strB.append(Constants.UAF_FORBID_ALLOW_CONCAT);
            strB.append(Constants.UAF_FORBID_ALLOW_CONCAT_SPLIT);
            strB.append(Constants.UAF_FORBID_ALLOW_CONCAT);
            String vendor = strB.toString();
            strB.delete(0, strB.length());

            // 根据渠道号查询黑名单，首先redis缓存查询，如果缓存中没有则数据库查询
            Set<String> forbid = getEquipment(appID);
            if (forbid.size() > 1) {
                LOG.debug("type:" + vendor_quipment_version + ",forbidSet:" + forbid);
                if (forbid.contains(vendor)){
                    throw new UAFException(UAFErrorCode.DEVICE_IN_BLACKLIST);
                }
                if (forbid.contains(vendor_quipment)){
                    throw new UAFException(UAFErrorCode.DEVICE_IN_BLACKLIST);
                }
                if (forbid.contains(vendor_quipment_version)) {
                    throw new UAFException(UAFErrorCode.DEVICE_IN_BLACKLIST);
                }
            }
            if (forbid.size() <= 1){
                // 查询全局黑名单，首先redis缓存查询，如果缓存中没有则数据库查询
                Set<String> globalForbid = getEquipment(Constants.UAF_FORBID_ALLOW_GLABAL_TENANT);
                if (globalForbid.size() > 1){
                    LOG.debug("type:" + vendor_quipment_version + ",globalForbidSet:" + globalForbid);
                    if (globalForbid.contains(vendor)){
                        throw new UAFException(UAFErrorCode.DEVICE_IN_BLACKLIST);
                    }
                    if (globalForbid.contains(vendor_quipment)){
                        throw new UAFException(UAFErrorCode.DEVICE_IN_BLACKLIST);
                    }
                    if (globalForbid.contains(vendor_quipment_version)) {
                        throw new UAFException(UAFErrorCode.DEVICE_IN_BLACKLIST);
                    }
                }
            }

            // 根据渠道号查询白名单，redis缓存查询，如果未查询到，则进行数据库查询
            Set<String> allow = getVendor(appID);
            if (allow.size() > 1){
                LOG.debug("type:" + vendor_quipment_version + ",allowSet:" + allow);
                if (!(allow.contains(vendor) || allow.contains(vendor_quipment)
                      || allow.contains(vendor_quipment_version))) {
                    throw new UAFException(UAFErrorCode.DEVICE_NOT_IN_WHITELIST);
                }
            }
            if (allow.size() <= 1){
                // 查询全局白名单，redis缓存查询，如果未查询到，则进行数据库查询
                Set<String> globalAllow = getVendor(Constants.UAF_FORBID_ALLOW_GLABAL_TENANT);
                if (globalAllow.size() > 1){
                    LOG.debug("type:" + vendor_quipment_version + ",globalAllowSet:" + globalAllow);
                    if (!(globalAllow.contains(vendor) || globalAllow.contains(vendor_quipment)
                            || globalAllow.contains(vendor_quipment_version))) {
                        throw new UAFException(UAFErrorCode.DEVICE_NOT_IN_WHITELIST);
                    }
                }
            }

            String deviceId = devices.getDeviceID();
            if (Strings.isNotBlank(deviceId)) {
                // 设备标识黑名单，首先redis缓存查询，不存在，数据库查询
                Set<String> deviceIds = getDeviceIds(appID);
                if (deviceIds.contains(deviceId)) {
                    throw new UAFException(UAFErrorCode.DEVICE_ID_IN_BLACKLIST);
                }
            }
        }
    }

    private Set<String> getDeviceIds(String appID) {
        UAFChallengeStoreServiceProvider uafChallengeStoreServiceProvider = GuiceUtil.getProcessorInjector()
                .getInstance(UAFChallengeStoreServiceProvider.class);
        ChallengeStoreService challengeStoreService = uafChallengeStoreServiceProvider.get();
        try {
            Set<String> devices = challengeStoreService.smembers(Constants.CACHE_DEVICE_FORBID + appID);
            if (devices.isEmpty()) {
                devices = new HashSet<String>();
                // dao
                Connection conn = null;
                try {
                    conn = UAFDBConnectionMgr.getConnection(true);
                    WhiteListDAO dao = UAFDAOFactory.createWhiteListDAO(conn);
                    devices.addAll(dao.getDeviceIdForbidList(appID));
                } catch (SQLException sqle) {
                    throw new DAOException(UAFErrorCode.DB_DATABASE_CONNECTION_ERROR, sqle);
                } finally {
                    DBUtil.close(conn);
                }
                devices.add("");
                LOG.debug("db deviceId forbid Set:" + devices);
                final int size = devices.size();
                String[] strs = new String[size];
                strs = devices.toArray(strs);
                challengeStoreService.sadd(Constants.CACHE_DEVICE_FORBID + appID, strs);
            }
            return devices;
        } catch (Exception e) {
            LOG.error("get deviceIds error", e);
        }
        return null;
    }

    /**
     * 厂商白名单
     *
     * @param appID
     * @return
     */
    private Set<String> getVendor(String appID) {
        UAFChallengeStoreServiceProvider uafChallengeStoreServiceProvider = GuiceUtil.getProcessorInjector()
                .getInstance(UAFChallengeStoreServiceProvider.class);
        ChallengeStoreService challengeStoreService = uafChallengeStoreServiceProvider.get();
        try {
            // 去redis缓存中获取厂商白名单
            Set<String> vendorSet = challengeStoreService.smembers(Constants.CACHE_VENDOR_ALLOW + appID);
            // 如果缓存中没有查询到则去数据库中查询
            if (vendorSet.isEmpty()) {
                vendorSet = new HashSet<String>();
                // dao
                Connection conn = null;
                try {
                    conn = UAFDBConnectionMgr.getConnection(true);
                    WhiteListDAO dao = UAFDAOFactory.createWhiteListDAO(conn);
                    vendorSet.addAll(dao.vendorList(appID));
                } catch (SQLException sqle) {
                    throw new DAOException(UAFErrorCode.DB_DATABASE_CONNECTION_ERROR, sqle);
                } finally {
                    DBUtil.close(conn);
                }
                vendorSet.add("");
                LOG.debug("db vendorSet:" + vendorSet);
                final int size = vendorSet.size();
                String[] strs = new String[size];
                strs = vendorSet.toArray(strs);
                // 数据库中查询的数据放到redis缓存中
                challengeStoreService.sadd(Constants.CACHE_VENDOR_ALLOW + appID, strs);
            }
            return vendorSet;
        } catch (Exception e) {
            LOG.error("get vendor error", e);
        }
        return null;
    }

    private Set<String> getEquipmentVersion(String deviceName) {
        UAFChallengeStoreServiceProvider uafChallengeStoreServiceProvider = GuiceUtil.getProcessorInjector()
                .getInstance(UAFChallengeStoreServiceProvider.class);
        ChallengeStoreService challengeStoreService = uafChallengeStoreServiceProvider.get();
        try {
            Set<String> equipmentVersionSet = challengeStoreService.smembers(Constants.CACHE_EQUIPMENT_FORBID + "version." + deviceName);
            if (equipmentVersionSet.isEmpty()) {
                equipmentVersionSet = new HashSet<String>();
                // dao
                Connection conn = null;
                try {
                    conn = UAFDBConnectionMgr.getConnection(true);
                    WhiteListDAO dao = UAFDAOFactory.createWhiteListDAO(conn);
                    equipmentVersionSet.addAll(dao.getEquipmentVersion(deviceName));
                } catch (SQLException sqle) {
                    throw new DAOException(UAFErrorCode.DB_DATABASE_CONNECTION_ERROR, sqle);
                } finally {
                    DBUtil.close(conn);
                }
                equipmentVersionSet.add("");
                LOG.debug("db equipmentVersionSet:" + equipmentVersionSet);
                final int size = equipmentVersionSet.size();
                String[] strs = new String[size];
                strs = equipmentVersionSet.toArray(strs);
                challengeStoreService.sadd(Constants.CACHE_EQUIPMENT_FORBID + "version." + deviceName, strs);
            }
            return equipmentVersionSet;
        } catch (Exception e) {
            LOG.error("get EquipmentVersion error", e);
        }
        return null;
    }

    /**
     * 设备黑名单
     *
     * @param appID
     * @return
     */
    private Set<String> getEquipment(String appID) {
        UAFChallengeStoreServiceProvider uafChallengeStoreServiceProvider = GuiceUtil.getProcessorInjector()
                .getInstance(UAFChallengeStoreServiceProvider.class);
        ChallengeStoreService challengeStoreService = uafChallengeStoreServiceProvider.get();
        try {
            Set<String> equipmentSet = challengeStoreService.smembers(Constants.CACHE_EQUIPMENT_FORBID + appID);
            if (equipmentSet.isEmpty()) {
                equipmentSet = new HashSet<String>();
                Connection conn = null;
                try {
                    conn = UAFDBConnectionMgr.getConnection(true);
                    WhiteListDAO dao = UAFDAOFactory.createWhiteListDAO(conn);
                    equipmentSet.addAll(dao.equipmentList(appID));
                } catch (SQLException sqle) {
                    throw new DAOException(UAFErrorCode.DB_DATABASE_CONNECTION_ERROR, sqle);
                } finally {
                    DBUtil.close(conn);
                }
                equipmentSet.add("");
                LOG.debug("db equipmentSet:" + equipmentSet);
                final int size = equipmentSet.size();
                String[] strs = new String[size];
                strs = equipmentSet.toArray(strs);
                challengeStoreService.sadd(Constants.CACHE_EQUIPMENT_FORBID + appID, strs);
            }
            return equipmentSet;
        } catch (Exception e) {
            LOG.error("get equipment error", e);
        }
        return null;
    }

    protected List<RegistrationRequest> checkVendorAccept(List<RegistrationRequest> requests, DeviceDetails devices) throws DAOException, ChallengeStoreException {

        if (null == devices || Strings.isNullOrEmpty(devices.getDeviceType())) {
            return requests;
        }
        UAFChallengeStoreServiceProvider uafChallengeStoreServiceProvider = GuiceUtil.getProcessorInjector()
                .getInstance(UAFChallengeStoreServiceProvider.class);
        ChallengeStoreService challengeStoreService = uafChallengeStoreServiceProvider.get();
        // 检查redis中是否有
        String vendorAcceptValue = challengeStoreService.getRedisValue(Constants.UAF_VENDOR_ACCEPT);
        if (null == vendorAcceptValue) {
            Connection conn = null;
            try {
                conn = UAFDBConnectionMgr.getConnection();
                UasConfigDAO uasConfigDAO = UAFDAOFactory.createUasConfigDAO(conn);
                // 数据库查询可以接受的厂商
                String vendorAccept = uasConfigDAO.getConfigByKey(Constants.UAF_VENDOR_ACCEPT);
                vendorAcceptValue = vendorAccept;
                if (Strings.isNullOrEmpty(vendorAccept)) {
                    vendorAccept = " ";
                }
                challengeStoreService.putString(Constants.UAF_VENDOR_ACCEPT, vendorAccept);

            } catch (SQLException sqle) {
                throw new DAOException(UAFErrorCode.DB_DATABASE_CONNECTION_ERROR, sqle);
            } finally {
                DBUtil.close(conn);
            }
        }
        if (Strings.isNotBlank(vendorAcceptValue)) {
            Map<String, Map<String, List<String>>> vendorAcceptMap = new HashMap<String, Map<String, List<String>>>();
            JsonReader reader = new JsonReader(new StringReader(vendorAcceptValue));
            try {
                reader.beginObject();
                while (reader.hasNext()) {
                    String tagName = reader.nextName();
                    // 读取出支持的厂商及具体的手机型号
                    Map<String, List<String>> vendorMap = readerArray(reader);
                    vendorAcceptMap.put(tagName, vendorMap);
                }
                reader.endObject();
            } catch (Exception e) {

            } finally {
                try {
                    reader.close();
                } catch (IOException e) {
                }
            }
            // 查看数据库中是否支持当前手机类型、手机名称
            for (RegistrationRequest registrationRequest : requests) {
                List<List<MatchCriteria>> acceptList = registrationRequest.getPolicy().getAccepted();
                Iterator<List<MatchCriteria>> iterator = acceptList.iterator();
                while (iterator.hasNext()) {
                    List<MatchCriteria> matchCriteriaList = iterator.next();
                    String aaId = matchCriteriaList.get(0).getAAID().get(0).format();
                    if (aaId.equals("004A#01AA") || aaId.equals("001A#3333")) {
                        if (vendorAcceptMap.containsKey(aaId)) {
                            Map<String, List<String>> vendorMap = vendorAcceptMap.get(aaId);
                            if (null != vendorMap && vendorMap.size() > 0) {
                                if (vendorMap.containsKey(devices.getDeviceType())) {
                                    List<String> vendorList = vendorMap.get(devices.getDeviceType());
                                    if (null != vendorList && vendorList.size() > 0) {
                                        if (!vendorList.contains(devices.getDeviceName())) {
                                            iterator.remove();
                                        }
                                    }
                                } else {
                                    iterator.remove();
                                }
                            }
                        }
                    }
                }
            }
        }
        return requests;
    }

    private Map<String, List<String>> readerArray(JsonReader reader) throws IOException {
        Map<String, List<String>> resultVendorMap = new HashMap<String, List<String>>();
        reader.beginArray();
        while (reader.hasNext()) {
            Map<String, List<String>> vendorMap = readerObject(reader);
            resultVendorMap.putAll(vendorMap);
        }
        reader.endArray();
        return resultVendorMap;
    }

    private Map<String, List<String>> readerObject(JsonReader reader) throws IOException {
        Map<String, List<String>> vendorMap = new HashMap<String, List<String>>();
        reader.beginObject();
        while (reader.hasNext()) {
            String tagName = reader.nextName();
            List<String> vendorList = readerArray2(reader);
            vendorMap.put(tagName, vendorList);
        }
        reader.endObject();
        return vendorMap;
    }

    private List<String> readerArray2(JsonReader reader) throws IOException {
        List<String> vendorList = new ArrayList<String>();
        reader.beginArray();
        while (reader.hasNext()) {
            String val = reader.nextString();
            if (Strings.isNotBlank(val)) {
                vendorList.add(val);
            }
        }
        reader.endArray();
        return vendorList;
    }

    public void checkUafRegStatus() throws UAFException {
        try {
            String regStatus = this.getServerConfig().getUasConfig(Constants.UAS_CONFIG_KEY_UAF_REG_STATUS);
            if (Strings.isNotBlank(regStatus)) {
                boolean regStatusBoolean = Boolean.parseBoolean(regStatus);
                if (!regStatusBoolean) {
                    throw new UAFException(UAFErrorCode.PROTOCOL_REG_CLOSED);
                }
            }
        } catch (ChallengeStoreException e) {
            LOG.error("checkUafRegStatus error", e);
        } catch (DAOException e) {
            LOG.error("checkUafRegStatus error", e);
        }
    }

    public void checkUafAuthStatus() throws UAFException {
        try {
            String authStatus = this.getServerConfig().getUasConfig(Constants.UAS_CONFIG_KEY_UAF_AUTH_STATUS);
            if (Strings.isNotBlank(authStatus)) {
                boolean authStatusBoolean = Boolean.parseBoolean(authStatus);
                if (!authStatusBoolean) {
                    throw new UAFException(UAFErrorCode.PROTOCOL_AUTH_CLOSED);
                }
            }
        } catch (ChallengeStoreException e) {
            LOG.error("checkUafAuthStatus error", e);
        } catch (DAOException e) {
            LOG.error("checkUafAuthStatus error", e);
        }
    }

    public void delCache(String appID, String deviceID, String hashedUserName, String authType, String transType) {
        String uniqueReqKey = "uaf.unique.req." + appID + "." + deviceID + "." + hashedUserName + "." + authType + "." + transType;
        if (authType.compareTo(Constants.AuthType.CERT_FINGERPRINT.getAuthType()) >= 0 && com.gmrz.util.Strings.isNotBlank(uniqueReqKey)) {
            ChallengeStoreService challengeStoreService = GuiceUtil.getProcessorInjector().getInstance(ChallengeStoreService.class);
            try {
                // 使用lua脚本实现redis的操作
                challengeStoreService.releaseDistributedLock(uniqueReqKey, Constants.LOCK_VALUE);
            } catch (ChallengeStoreException e) {
                LOG.error("releaseDistributedLock error", e);
            }
        }
    }
}
